/*
 * Released under BSD License
 * Copyright (c) 2014-2015 hizzgdev@163.com
 *
 * Project Home:
 *   https://github.com/hizzgdev/jsmind/
 */

(function($w) {
  'use strict'

  var __name__ = 'jsMind'
  var jsMind = $w[__name__]
  if (!jsMind) { return }
  if (typeof jsMind.screenshot !== 'undefined') { return }
  var $d = $w.document
  var $c = function(tag) { return $d.createElement(tag) }

  var css = function(cstyle, property_name) {
    return cstyle.getPropertyValue(property_name)
  }
  var is_visible = function(cstyle) {
    var visibility = css(cstyle, 'visibility')
    var display = css(cstyle, 'display')
    return (visibility !== 'hidden' && display !== 'none')
  }
  var jcanvas = {}
  jcanvas.rect = function(ctx, x, y, w, h, r) {
    if (w < 2 * r) r = w / 2
    if (h < 2 * r) r = h / 2
    ctx.moveTo(x + r, y)
    ctx.arcTo(x + w, y, x + w, y + h, r)
    ctx.arcTo(x + w, y + h, x, y + h, r)
    ctx.arcTo(x, y + h, x, y, r)
    ctx.arcTo(x, y, x + w, y, r)
  }

  jcanvas.text_multiline = function(ctx, text, x, y, w, h, lineheight) {
    var line = ''
    var text_len = text.length
    var chars = text.split('')
    var test_line = null
    ctx.textAlign = 'left'
    ctx.textBaseline = 'top'
    for (var i = 0; i < text_len; i++) {
      test_line = line + chars[i]
      if (ctx.measureText(test_line).width > w && i > 0) {
        ctx.fillText(line, x, y)
        line = chars[i]
        y += lineheight
      } else {
        line = test_line
      }
    }
    ctx.fillText(line, x, y)
  }

  jcanvas.text_ellipsis = function(ctx, text, x, y, w, h) {
    var center_y = y + h / 2
    var text = jcanvas.fittingString(ctx, text, w)
    ctx.textAlign = 'left'
    ctx.textBaseline = 'middle'
    ctx.fillText(text, x, center_y, w)
  }

  jcanvas.fittingString = function(ctx, text, max_width) {
    var width = ctx.measureText(text).width
    var ellipsis = '…'
    var ellipsis_width = ctx.measureText(ellipsis).width
    if (width <= max_width || width <= ellipsis_width) {
      return text
    } else {
      var len = text.length
      while (width >= max_width - ellipsis_width && len-- > 0) {
        text = text.substring(0, len)
        width = ctx.measureText(text).width
      }
      return text + ellipsis
    }
  }

  jcanvas.image = function(ctx, url, x, y, w, h, r, rotation, callback) {
    var img = new Image()
    img.onload = function() {
      ctx.save()
      ctx.translate(x, y)
      ctx.save()
      ctx.beginPath()
      jcanvas.rect(ctx, 0, 0, w, h, r)
      ctx.closePath()
      ctx.clip()
      ctx.translate(w / 2, h / 2)
      ctx.rotate(rotation * Math.PI / 180)
      ctx.drawImage(img, -w / 2, -h / 2)
      ctx.restore()
      ctx.restore()
      !!callback && callback()
    }
    img.src = url
  }

  jsMind.screenshot = function(jm) {
    this.jm = jm
    this.canvas_elem = null
    this.canvas_ctx = null
    this._inited = false
  }

  jsMind.screenshot.prototype = {
    init: function() {
      if (this._inited) { return }
      console.log('init')
      var c = $c('canvas')
      var ctx = c.getContext('2d')

      this.canvas_elem = c
      this.canvas_ctx = ctx
      this.jm.view.e_panel.appendChild(c)
      this._inited = true
      this.resize()
    },

    shoot: function(callback) {
      this.init()
      this._draw(function() {
        !!callback && callback()
        this.clean()
      }.bind(this))
      this._watermark()
    },

    shootDownload: function() {
      this.shoot(function() {
        this._download()
      }.bind(this))
    },

    shootAsDataURL: function(callback) {
      this.shoot(function() {
        !!callback && callback(this.canvas_elem.toDataURL())
      }.bind(this))
    },

    resize: function() {
      if (this._inited) {
        this.canvas_elem.width = this.jm.view.size.w
        this.canvas_elem.height = this.jm.view.size.h
      }
    },

    clean: function() {
      var c = this.canvas_elem
      this.canvas_ctx.clearRect(0, 0, c.width, c.height)
    },

    _draw: function(callback) {
      var ctx = this.canvas_ctx
      ctx.textAlign = 'left'
      ctx.textBaseline = 'top'
      this._draw_lines(function() {
        this._draw_nodes(callback)
      }.bind(this))
    },

    _watermark: function() {
      var c = this.canvas_elem
      var ctx = this.canvas_ctx
      ctx.textAlign = 'right'
      ctx.textBaseline = 'bottom'
      ctx.fillStyle = '#000'
      ctx.font = '11px Verdana,Arial,Helvetica,sans-serif'
      ctx.fillText('hizzgdev.github.io/jsmind', c.width - 5.5, c.height - 2.5)
      ctx.textAlign = 'left'
      ctx.fillText($w.location, 5.5, c.height - 2.5)
    },

    _draw_lines: function(callback) {
      this.jm.view.graph.copy_to(this.canvas_ctx, callback)
    },

    _draw_nodes: function(callback) {
      var nodes = this.jm.mind.nodes
      var node
      for (var nodeid in nodes) {
        node = nodes[nodeid]
        this._draw_node(node)
      }

      function check_nodes_ready() {
        console.log('check_node_ready' + new Date())
        var allOk = true
        for (var nodeid in nodes) {
          node = nodes[nodeid]
          allOk = allOk & node.ready
        }

        if (!allOk) {
          $w.setTimeout(check_nodes_ready, 200)
        } else {
          $w.setTimeout(callback, 200)
        }
      }
      check_nodes_ready()
    },

    _draw_node: function(node) {
      var ctx = this.canvas_ctx
      var view_data = node._data.view
      var node_element = view_data.element
      var ncs = getComputedStyle(node_element)
      if (!is_visible(ncs)) {
        node.ready = true
        return
      }

      var bgcolor = css(ncs, 'background-color')
      var round_radius = parseInt(css(ncs, 'border-top-left-radius'))
      var color = css(ncs, 'color')
      var padding_left = parseInt(css(ncs, 'padding-left'))
      var padding_right = parseInt(css(ncs, 'padding-right'))
      var padding_top = parseInt(css(ncs, 'padding-top'))
      var padding_bottom = parseInt(css(ncs, 'padding-bottom'))
      var text_overflow = css(ncs, 'text-overflow')
      var font = css(ncs, 'font-style') + ' ' +
                css(ncs, 'font-variant') + ' ' +
                css(ncs, 'font-weight') + ' ' +
                css(ncs, 'font-size') + '/' + css(ncs, 'line-height') + ' ' +
                css(ncs, 'font-family')

      var rb = {
        x: view_data.abs_x,
        y: view_data.abs_y,
        w: view_data.width + 1,
        h: view_data.height + 1
      }
      var tb = {
        x: rb.x + padding_left,
        y: rb.y + padding_top,
        w: rb.w - padding_left - padding_right,
        h: rb.h - padding_top - padding_bottom
      }

      ctx.font = font
      ctx.fillStyle = bgcolor
      ctx.beginPath()
      jcanvas.rect(ctx, rb.x, rb.y, rb.w, rb.h, round_radius)
      ctx.closePath()
      ctx.fill()

      ctx.fillStyle = color
      if ('background-image' in node.data) {
        var backgroundUrl = css(ncs, 'background-image').slice(5, -2)
        node.ready = false
        var rotation = 0
        if ('background-rotation' in node.data) {
          rotation = node.data['background-rotation']
        }
        jcanvas.image(ctx, backgroundUrl, rb.x, rb.y, rb.w, rb.h, round_radius, rotation,
          function() {
            node.ready = true
          })
      }
      if (node.topic) {
        if (text_overflow === 'ellipsis') {
          jcanvas.text_ellipsis(ctx, node.topic, tb.x, tb.y, tb.w, tb.h)
        } else {
          var line_height = parseInt(css(ncs, 'line-height'))
          jcanvas.text_multiline(ctx, node.topic, tb.x, tb.y, tb.w, tb.h, line_height)
        }
      }
      if (view_data.expander) {
        this._draw_expander(view_data.expander)
      }
      if (!('background-image' in node.data)) {
        node.ready = true
      }
    },

    _draw_expander: function(expander) {
      var ctx = this.canvas_ctx
      var ncs = getComputedStyle(expander)
      if (!is_visible(ncs)) { return }

      var style_left = css(ncs, 'left')
      var style_top = css(ncs, 'top')
      var font = css(ncs, 'font')
      var left = parseInt(style_left)
      var top = parseInt(style_top)
      var is_plus = expander.innerHTML === '+'

      ctx.lineWidth = 1

      ctx.beginPath()
      ctx.arc(left + 7, top + 7, 5, 0, Math.PI * 2, true)
      ctx.moveTo(left + 10, top + 7)
      ctx.lineTo(left + 4, top + 7)
      if (is_plus) {
        ctx.moveTo(left + 7, top + 4)
        ctx.lineTo(left + 7, top + 10)
      }
      ctx.closePath()
      ctx.stroke()
    },

    _download: function() {
      var c = this.canvas_elem
      var name = this.jm.mind.name + '.png'

      if (navigator.msSaveBlob && (!!c.msToBlob)) {
        var blob = c.msToBlob()
        navigator.msSaveBlob(blob, name)
      } else {
        var bloburl = this.canvas_elem.toDataURL()
        var anchor = $c('a')
        if ('download' in anchor) {
          anchor.style.visibility = 'hidden'
          anchor.href = bloburl
          anchor.download = name
          $d.body.appendChild(anchor)
          var evt = $d.createEvent('MouseEvents')
          evt.initEvent('click', true, true)
          anchor.dispatchEvent(evt)
          $d.body.removeChild(anchor)
        } else {
          location.href = bloburl
        }
      }
    },

    jm_event_handle: function(type, data) {
      if (type === jsMind.event_type.resize) {
        this.resize()
      }
    }
  }

  var screenshot_plugin = new jsMind.plugin('screenshot', function(jm) {
    var jss = new jsMind.screenshot(jm)
    jm.screenshot = jss
    jm.shoot = function() {
      jss.shoot()
    }
    jm.add_event_listener(function(type, data) {
      jss.jm_event_handle.call(jss, type, data)
    })
  })

  jsMind.register_plugin(screenshot_plugin)
})(window)
